// Copyright (c) 2006-2012 Google, Inc. and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Author: Kenton Varda

#include "evlan/public/evaluator.h"

#include <typeinfo>

#include "evlan/common.h"
#include "evlan/common.h"
#include "evlan/common.h"
#include "evlan/parser/parser.h"
#include "evlan/parser/parser.h"
#include "evlan/proto/module.pb.h"
#include "evlan/public/cpp_glue.h"
#include "evlan/public/import_resolver.h"
#include "evlan/vm/builtin/array.h"
#include "evlan/vm/builtin/atom.h"
#include "evlan/vm/builtin/boolean.h"
#include "evlan/vm/builtin/builtin.h"
#include "evlan/vm/builtin/bytes.h"
#include "evlan/vm/builtin/integer.h"
#include "evlan/vm/builtin/double.h"
#include "evlan/vm/builtin/protobuf.h"
#include "evlan/vm/builtin/support.h"
#include "evlan/vm/builtin/type_error.h"
#include "evlan/vm/interpreter/interpreter.h"
#include "evlan/vm/memory/allocator.h"
#include "evlan/vm/memory/braindead_memory_manager.h"
#include "evlan/vm/memory/debug_memory_manager.h"
#include "evlan/vm/memory/memory_manager.h"
#include "evlan/vm/memory/pool_allocator.h"
#include "evlan/vm/runtime.h"
#include <google/protobuf/io/printer.h>
#include "evlan/stringpiece.h"
#include "evlan/stl-util.h"

namespace evlan {

using namespace std::placeholders;

// Defined in evlan/api/builtinSupport.cc, when generated by evlanc from
// builtinSupport.evlan.
const StringPiece GetBuiltinSupportBytecode();

StringPiece AtomId::GetName() {
  return vm::builtin::GetAtomName(vm::Data(ptr_));
}

// ===================================================================

CustomValue::~CustomValue() {}

string CustomValue::DebugString(int depth) {
  string result("customValue(\"");
  result += typeid(this).name();    // C++ class name.
  result += "\")";
  return result;
}

class EvlanEvaluator::TrackedCustomValue : public vm::memory::TrackedObject {
 public:
  TrackedCustomValue(vm::memory::MemoryManager* memory_manager,
                     CustomValue* value)
    : value_(value),
      tracker_(memory_manager->AddTrackedObject(this)) {}
  virtual ~TrackedCustomValue() {}

  inline CustomValue* Get() const { return value_.get(); }

  void Run(vm::Context* context, vm::Value self) const {
    // The CustomValue may use the MemoryManager during its execution, so
    // we need to make sure the callback is not garbage collected in the
    // meantime.
    vm::KeepAliveValue* callback =
      new vm::KeepAliveValue(context->GetMemoryManager(),
                             context->GetCallback());

    // Call the CustomValue.
    EvlanEvaluator* evaluator = context->GetEvlanEvaluator();
    scoped_ptr<EvlanValue> wrapped_self(
      new EvlanValue(evaluator, self, context->GetMemoryManager()));
    scoped_ptr<EvlanValue> parameter(
      new EvlanValue(evaluator, context->GetParameter(),
                     context->GetMemoryManager()));
    scoped_ptr<EvlanValue> result(
      value_.get()->Call(evaluator, wrapped_self.get(), parameter.get()));

    // Return.
    context->SetNext(callback->Get(NULL),
                     result->value_->Get(context->GetMemoryManager()));
    callback->Release();
  }

  inline void VisitTracker(vm::memory::MemoryVisitor* visitor) const {
    visitor->VisitTrackedObject(tracker_);
  }

  virtual void DebugPrint(int depth, google::protobuf::io::Printer* printer) const {
    printer->Print("$text$", "text", value_->DebugString(depth));
  }

  // implements TrackedObject --------------------------------------
  virtual void NotReachable() { delete this; }
  virtual void Accept(vm::memory::MemoryVisitor* visitor) const {
    // Nothing.
  }

 private:
  scoped_ptr<CustomValue> value_;
  vm::memory::ObjectTracker* tracker_;
};

class EvlanEvaluator::CustomValueLogic : public vm::Logic {
 public:
  CustomValueLogic() {}
  virtual ~CustomValueLogic() {}

  static CustomValueLogic kSingleton;

  // implements Logic ------------------------------------------------
  virtual void Run(vm::Context* context) const {
    context->GetData().As<TrackedCustomValue>()->Run(context,
      vm::Value(this, context->GetData()));
  }
  virtual void Accept(const vm::Data* data,
                      vm::memory::MemoryVisitor* visitor) const {
    data->As<TrackedCustomValue>()->VisitTracker(visitor);
  }
  virtual void DebugPrint(vm::Data data, int depth,
                          google::protobuf::io::Printer* printer) const {
    data.As<TrackedCustomValue>()->DebugPrint(depth, printer);
  }
};

EvlanEvaluator::CustomValueLogic EvlanEvaluator::CustomValueLogic::kSingleton;

// ===================================================================

NonBlockingCustomValue::~NonBlockingCustomValue() {}

string NonBlockingCustomValue::DebugString(int depth) {
  string result("nonBlockingCustomValue(\"");
  result += typeid(this).name();    // C++ class name.
  result += "\")";
  return result;
}

class EvlanEvaluator::TrackedNonBlockingCustomValue
    : public vm::memory::TrackedObject {
 public:
  TrackedNonBlockingCustomValue(vm::memory::MemoryManager* memory_manager,
                                NonBlockingCustomValue* value)
    : value_(value),
      tracker_(memory_manager->AddTrackedObject(this)) {}
  virtual ~TrackedNonBlockingCustomValue() {}

  inline NonBlockingCustomValue* Get() const { return value_.get(); }

  void Run(vm::Context* context, vm::Value self) const {
    // Save the callback and ContextFactory for later -- we need to use them
    // when the non-blocking call completes.
    vm::KeepAliveValue* callback =
      new vm::KeepAliveValue(context->GetMemoryManager(),
                             context->GetCallback());

    vm::ContextFactory* context_factory = context->GetContextFactory();
    context_factory->AddReference();

    // Make sure this object cannot be garbage collected while running.
    tracker_->AddExternalReference();

    // Call the NonBlockingCustomValue.
    EvlanEvaluator* evaluator = context->GetEvlanEvaluator();
    scoped_ptr<EvlanValue> wrapped_self(
      new EvlanValue(evaluator, self, context->GetMemoryManager()));
    scoped_ptr<EvlanValue> parameter(
      new EvlanValue(evaluator, context->GetParameter(),
                     context->GetMemoryManager()));
    value_.get()->CallNonBlocking(
        evaluator, wrapped_self.get(), parameter.get(),
        std::bind(&TrackedNonBlockingCustomValue::Done, this,
                  context_factory, callback, _1));
  }

  // For a good time, remove the "const" qualifier from this method, compile,
  // and marvel at the incomprehensible error message.
  void Done(vm::ContextFactory* context_factory,
            vm::KeepAliveValue* callback,
            EvlanValue* result) const {
    // Create a new context to run the final callback.
    vm::Context context(context_factory);
    context.SetNext(callback->Get(context.GetMemoryManager()),
                    result->value_->Get(context.GetMemoryManager()));

    // Clean up after ourselves before actually running the callback.
    context_factory->RemoveReference();
    tracker_->RemoveExternalReference();
    callback->Release();
    delete result;

    context.Run();
  }

  inline void VisitTracker(vm::memory::MemoryVisitor* visitor) const {
    visitor->VisitTrackedObject(tracker_);
  }

  virtual void DebugPrint(int depth, google::protobuf::io::Printer* printer) const {
    printer->Print("$text$", "text", value_->DebugString(depth));
  }

  // implements TrackedObject --------------------------------------
  virtual void NotReachable() { delete this; }
  virtual void Accept(vm::memory::MemoryVisitor* visitor) const {
    // Nothing.
  }

 private:
  scoped_ptr<NonBlockingCustomValue> value_;
  vm::memory::ObjectTracker* tracker_;
};

class EvlanEvaluator::NonBlockingCustomValueLogic : public vm::Logic {
 public:
  NonBlockingCustomValueLogic() {}
  virtual ~NonBlockingCustomValueLogic() {}

  static NonBlockingCustomValueLogic kSingleton;

  // implements Logic ------------------------------------------------
  virtual void Run(vm::Context* context) const {
    context->GetData().As<TrackedNonBlockingCustomValue>()->Run(context,
      vm::Value(this, context->GetData()));
  }
  virtual void Accept(const vm::Data* data,
                      vm::memory::MemoryVisitor* visitor) const {
    data->As<TrackedNonBlockingCustomValue>()->VisitTracker(visitor);
  }
  virtual void DebugPrint(vm::Data data, int depth,
                          google::protobuf::io::Printer* printer) const {
    data.As<TrackedNonBlockingCustomValue>()->DebugPrint(depth, printer);
  }
};

EvlanEvaluator::NonBlockingCustomValueLogic
    EvlanEvaluator::NonBlockingCustomValueLogic::kSingleton;

// ===================================================================

class EvlanValue::DoneCallback : public vm::Logic {
 public:
  DoneCallback() {}
  ~DoneCallback() {}

  static DoneCallback kSingleton;

  // implements Logic ------------------------------------------------
  void Run(vm::Context* context) const {
    EvlanValue* value = context->GetData().AsMutable<EvlanValue>();
    MutexLock lock(&value->evaluator_->mutex_);
    GOOGLE_CHECK(value->value_ == NULL) << "Callback called multiple times?";
    value->value_ = new vm::KeepAliveValue(context->GetMemoryManager(),
                                           context->GetParameter());
  }

  void Accept(const vm::Data* data, vm::memory::MemoryVisitor* visitor) const {
    // Nothing.
  }

  void DebugPrint(vm::Data data, int depth,
                  google::protobuf::io::Printer* printer) const {
    printer->Print("doneCallback");
  }
};

EvlanValue::DoneCallback EvlanValue::DoneCallback::kSingleton;

class EvlanValue::NonBlockingDoneCallback : public vm::Logic {
 public:
  NonBlockingDoneCallback() {}
  ~NonBlockingDoneCallback() {}

  static NonBlockingDoneCallback kSingleton;

  // implements Logic ------------------------------------------------
  void Run(vm::Context* context) const {
    function<void(EvlanValue*)>* callback =
        context->GetData().AsMutable<function<void(EvlanValue*)> >();
    (*callback)(new EvlanValue(context->GetEvlanEvaluator(),
                               context->GetParameter(),
                               context->GetMemoryManager()));
    delete callback;
  }

  void Accept(const vm::Data* data, vm::memory::MemoryVisitor* visitor) const {
    // Nothing.
  }

  void DebugPrint(vm::Data data, int depth,
                  google::protobuf::io::Printer* printer) const {
    printer->Print("nonBlockingDoneCallback");
  }
};

EvlanValue::NonBlockingDoneCallback
    EvlanValue::NonBlockingDoneCallback::kSingleton;

// ===================================================================

EvlanValue::EvlanValue(EvlanEvaluator* evaluator)
  : evaluator_(evaluator),
    value_(NULL) {
  evaluator_->mutex_.AssertHeld();
}

EvlanValue::EvlanValue(EvlanEvaluator* evaluator, vm::Value value)
  : evaluator_(evaluator),
    value_(new vm::KeepAliveValue(evaluator->memory_manager_.get(), value)) {
  evaluator_->mutex_.AssertHeld();
}

EvlanValue::EvlanValue(EvlanEvaluator* evaluator, vm::Value value,
                       vm::memory::MemoryManager* value_owner)
  : evaluator_(evaluator),
    value_(new vm::KeepAliveValue(value_owner, value)) {}

EvlanValue::~EvlanValue() {
  // NO LOCK:  RemoveExternalReference() can be called from any thread.

  // If the evaluator is currently shutting down, we do *not* want to call
  // value_->Release(), because value_ may already have been destroyed.  If
  // it hasn't been destroyed it will be destroyed shortly anyway, whether
  // we release it or not.
  if (value_ != NULL && !evaluator_->shutting_down_) {
    value_->Release();
  }
}

EvlanValue* EvlanValue::Clone() {
  MutexLock lock(&evaluator_->mutex_);
  return new EvlanValue(evaluator_, GetValue());
}

EvlanValue* EvlanValue::InternalCall(EvlanValue* parameter,
                                     function<void(EvlanValue*)> callback) {
  // Find the evaluator in which this value and the parameter are both valid.
  // We do our computation there.
  EvlanEvaluator* common_evaluator =
    evaluator_->FindCommonEvaluator(parameter->evaluator_);
  GOOGLE_CHECK(common_evaluator != NULL)
    << "Function and parameter are from independent EvlanEvaluators.";

  MutexLock lock(&common_evaluator->mutex_);

  common_evaluator->CheckNotForked();

  // Invoke this value on the parameter.
  return InternalCall(common_evaluator, parameter->GetValue(), callback);
}

EvlanValue* EvlanValue::InternalCall(const vector<EvlanValue*>& parameters,
                                     function<void(EvlanValue*)> callback) {
  if (parameters.size() == 1) {
    return InternalCall(parameters[0], callback);
  }

  // Find an evaluator in which this value and all parameters are valid.  We
  // do our computation there.
  EvlanEvaluator* common_evaluator = evaluator_;
  for (int i = 0; i < parameters.size(); i++) {
    common_evaluator =
      common_evaluator->FindCommonEvaluator(parameters[i]->evaluator_);
    GOOGLE_CHECK(common_evaluator != NULL)
      << "Function and parameter are from independent EvlanEvaluators.";
  }

  MutexLock lock(&common_evaluator->mutex_);
  common_evaluator->CheckNotForked();

  // Construct a tuple containing all the parameters, and then invoke this
  // value on it.
  vm::memory::NullMemoryRoot root;
  vm::Value* array =
    common_evaluator->memory_manager_->AllocateArray<vm::Value>(
      parameters.size(), &root);
  for (int i = 0; i < parameters.size(); i++) {
    array[i] = parameters[i]->GetValue();
  }
  vm::Value array_value = vm::builtin::ArrayUtils::AliasArray(
    common_evaluator->memory_manager_.get(), &root, parameters.size(), array);
  return InternalCall(common_evaluator,
                      vm::builtin::ArrayUtils::ArrayToTuple(array_value),
                      callback);
}

EvlanValue* EvlanValue::InternalGetMember(const StringPiece& name,
                                          function<void(EvlanValue*)> callback) {
  MutexLock lock(&evaluator_->mutex_);

  evaluator_->CheckNotForked();
  return InternalCall(evaluator_,
    evaluator_->atom_registry_->GetAtom(
      evaluator_->memory_manager_.get(), name),
    callback);
}

EvlanValue* EvlanValue::InternalCall(EvlanEvaluator* evaluator,
                                     const vm::Value& parameter,
                                     function<void(EvlanValue*)> callback) {
  evaluator->mutex_.AssertHeld();

  vm::Context context(evaluator->context_factory_.get());

  EvlanValue* result = NULL;
  vm::Value callback_value;
  if (callback == NULL) {
    result = new EvlanValue(evaluator);
    callback_value = vm::Value(&EvlanValue::DoneCallback::kSingleton,
                               vm::Data(result));
  } else {
    callback_value = vm::Value(&EvlanValue::NonBlockingDoneCallback::kSingleton,
                               vm::Data(new function<void(EvlanValue*)>(callback)));
  }

  // The parameter is owned by the Evaluator's MemoryManager.  We have to
  // transfer it to the one for this context.
  // TODO(kenton):  Provide a better way to do this?
  vm::KeepAliveValue* parameter_copy =
      new vm::KeepAliveValue(evaluator->memory_manager_.get(), parameter);
  context.SetNext(GetValue(), parameter_copy->Get(context.GetMemoryManager()),
                  callback_value);
  parameter_copy->Release();

  // Unlock while running the function so that other stuff can happen in
  // parallel.
  evaluator->mutex_.Unlock();
  context.Run();
  evaluator->mutex_.Lock();

  if (callback == NULL) {
    // Block until result is set.
    // TODO(kenton):  Actually unlock mutex_ and wait if IsReady() is false.
    GOOGLE_CHECK(result->IsReady());

    return result;
  } else {
    return NULL;
  }
}

bool EvlanValue::IsReady() {
  return value_ != NULL;
}

vm::Value EvlanValue::GetValue() {
  return value_->Get(evaluator_->memory_manager_.get());
}

EvlanValue::Type EvlanValue::GetType() {
  // In theory we don't need to lock here because all the functions below simply
  // check if the value's Logic pointer points at some singleton.  The
  // singletons can't move so the logic pointers can't change.  However, it's
  // probably better to be future-proof.
  MutexLock lock(&evaluator_->mutex_);

  vm::Value raw_value = GetValue();

  if (vm::builtin::BooleanUtils::IsBoolean(raw_value)) {
    return BOOLEAN;
  } else if (vm::builtin::IsInteger(raw_value)) {
    return INTEGER;
  } else if (vm::builtin::IsDouble(raw_value)) {
    return DOUBLE;
  } else if (vm::builtin::IsAtom(raw_value)) {
    return ATOM;
  } else if (vm::builtin::ByteUtils::IsByteArray(raw_value)) {
    return STRING;
  } else if (vm::builtin::ProtobufUtils::IsMessage(raw_value)) {
    return PROTOBUF;
  } else if (raw_value.GetLogic() ==
             &EvlanEvaluator::CustomValueLogic::kSingleton) {
    return CUSTOM_VALUE;
  } else if (raw_value.GetLogic() ==
             &EvlanEvaluator::NonBlockingCustomValueLogic::kSingleton) {
    return NON_BLOCKING_CUSTOM_VALUE;
  } else if (vm::builtin::TypeErrorUtils::IsError(raw_value)) {
    return TYPE_ERROR;
  } else {
    return OTHER;
  }
}

bool EvlanValue::GetBooleanValue(bool* value) {
  // This lock probably isn't necessary to read a simple value since no
  // pointers to relocatable memory are involved.  But we want to be
  // future-proof.
  MutexLock lock(&evaluator_->mutex_);
  if (!vm::builtin::BooleanUtils::IsBoolean(GetValue())) return false;
  *value = vm::builtin::BooleanUtils::GetBooleanValue(GetValue());
  return true;
}

bool EvlanValue::GetIntegerValue(int* value) {
  MutexLock lock(&evaluator_->mutex_);  // See comment in GetBooleanValue().
  if (!vm::builtin::IsInteger(GetValue())) return false;
  *value = vm::builtin::GetIntegerValue(GetValue());
  return true;
}

bool EvlanValue::GetDoubleValue(double* value) {
  MutexLock lock(&evaluator_->mutex_);  // See comment in GetBooleanValue().
  if (vm::builtin::IsDouble(GetValue())) {
    *value = vm::builtin::GetDoubleValue(GetValue());
    return true;
  } else if (vm::builtin::IsInteger(GetValue())) {
    *value = vm::builtin::GetIntegerValue(GetValue());
    return true;
  } else {
    return false;
  }
}

bool EvlanValue::GetAtomValue(StringPiece* value) {
  MutexLock lock(&evaluator_->mutex_);  // See comment in GetBooleanValue().
  if (!vm::builtin::IsAtom(GetValue())) return false;
  *value = vm::builtin::GetAtomName(GetValue().GetData());
  return true;
}

bool EvlanValue::GetAtomValue(AtomId* value) {
  MutexLock lock(&evaluator_->mutex_);  // See comment in GetBooleanValue().
  if (!vm::builtin::IsAtom(GetValue())) return false;
  *value = AtomId(GetValue().GetData().As<void>());
  return true;
}

bool EvlanValue::GetPermanentAtomValue(AtomId* value) {
  MutexLock lock(&evaluator_->mutex_);  // See comment in GetBooleanValue().
  if (!vm::builtin::IsAtom(GetValue())) return false;
  // Add a reference so that this value will not be destroyed until the
  // EvlanEvaluator is destroyed.
  value_->AddReference();
  *value = AtomId(GetValue().GetData().As<void>());
  return true;
}

bool EvlanValue::GetStringValue(StringPiece* value) {
  MutexLock lock(&evaluator_->mutex_);  // See comment in GetBooleanValue().
  if (!vm::builtin::ByteUtils::IsByteArray(GetValue())) return false;
  *value = vm::builtin::ByteUtils::ToString(GetValue());
  return true;
}

bool EvlanValue::GetProtobufValue(const google::protobuf::Message** value) {
  MutexLock lock(&evaluator_->mutex_);  // See comment in GetBooleanValue().
  if (!vm::builtin::ProtobufUtils::IsMessage(GetValue())) return false;
  *value = vm::builtin::ProtobufUtils::GetMessage(GetValue());
  return true;
}

bool EvlanValue::GetCustomValue(CustomValue** value) {
  MutexLock lock(&evaluator_->mutex_);  // See comment in GetBooleanValue().
  if (GetValue().GetLogic() !=
      &EvlanEvaluator::CustomValueLogic::kSingleton) {
    return false;
  }
  *value =
    GetValue().GetData().As<EvlanEvaluator::TrackedCustomValue>()->Get();
  return true;
}

bool EvlanValue::GetNonBlockingCustomValue(NonBlockingCustomValue** value) {
  MutexLock lock(&evaluator_->mutex_);  // See comment in GetBooleanValue().
  if (GetValue().GetLogic() !=
      &EvlanEvaluator::NonBlockingCustomValueLogic::kSingleton) {
    return false;
  }
  *value =
    GetValue().GetData()
              .As<EvlanEvaluator::TrackedNonBlockingCustomValue>()
             ->Get();
  return true;
}

bool EvlanValue::GetTypeErrorDescription(StringPiece* description) {
  MutexLock lock(&evaluator_->mutex_);  // See comment in GetBooleanValue().
  if (!vm::builtin::TypeErrorUtils::IsError(GetValue())) return false;
  *description = vm::builtin::TypeErrorUtils::GetDescription(GetValue());
  return true;
}

int EvlanValue::GetTupleSize() {
  MutexLock lock(&evaluator_->mutex_);  // See comment in GetBooleanValue().
  if (vm::builtin::ArrayUtils::IsTuple(GetValue())) {
    return vm::builtin::ArrayUtils::GetArraySize(GetValue());
  } else {
    return 1;
  }
}

bool EvlanValue::GetTupleContents(int size, EvlanValue* values[]) {
  if (size == 1) {
    values[0] = Clone();
    return true;
  } else {
    MutexLock lock(&evaluator_->mutex_);
    if (vm::builtin::ArrayUtils::IsTuple(GetValue())) {
      if (size != vm::builtin::ArrayUtils::GetArraySize(GetValue())) {
        return false;
      }

      for (int i = 0; i < size; i++) {
        values[i] = new EvlanValue(evaluator_,
          vm::builtin::ArrayUtils::GetElement(GetValue(), i));
      }
      return true;
    } else {
      return false;
    }
  }
}

bool EvlanValue::GetTupleContents(int size, scoped_ptr<EvlanValue> values[]) {
  if (size == 1) {
    values[0].reset(Clone());
    return true;
  } else {
    MutexLock lock(&evaluator_->mutex_);
    if (vm::builtin::ArrayUtils::IsTuple(GetValue())) {
      if (size != vm::builtin::ArrayUtils::GetArraySize(GetValue())) {
        return false;
      }

      for (int i = 0; i < size; i++) {
        values[i].reset(new EvlanValue(evaluator_,
          vm::builtin::ArrayUtils::GetElement(GetValue(), i)));
      }
      return true;
    } else {
      return false;
    }
  }
}

bool EvlanValue::GetArrayElements(vector<EvlanValue*>* elements) {
  MutexLock lock(&evaluator_->mutex_);
  vm::Value array_value = GetValue();
  if (!vm::builtin::ArrayUtils::IsArray(array_value)) return false;
  elements->resize(vm::builtin::ArrayUtils::GetArraySize(array_value));
  for (int i = 0; i < elements->size(); i++) {
    (*elements)[i] = new EvlanValue(evaluator_,
      vm::builtin::ArrayUtils::GetElement(array_value, i));
  }
  return true;
}

string EvlanValue::DebugString(int depth) {
  MutexLock lock(&evaluator_->mutex_);
  return GetValue().DebugString(depth);
}

vm::Value EvlanValue::InternalGetValue(
    vm::memory::MemoryManager* memory_manager) {
  return value_->Get(memory_manager);
}

// ===================================================================

class EvlanEvaluator::ContextFactoryImpl : public vm::ContextFactory {
 public:
  enum Mode {
    SINGLE_CONTEXT,
    MULTIPLE_CONTEXTS
  };

  ContextFactoryImpl(EvlanEvaluator* evaluator,
                     Mode mode)
      : mode_(mode), evaluator_(evaluator), refcount_(0),
        main_memory_manager_in_use_(false) {}

  virtual ~ContextFactoryImpl() {
    GOOGLE_CHECK_EQ(refcount_, 0)
        << "EvlanEvaluator destroyed while stuff was still running.";
    GOOGLE_CHECK(!main_memory_manager_in_use_);
    STLDeleteElements(&additional_memory_managers_);
    STLDeleteElements(&allocators_);
  }

  // implements ContextFactory ---------------------------------------

  virtual void AddReference() {
    MutexLock lock(&mutex_);
    ++refcount_;
  }

  virtual void RemoveReference() {
    // TODO(kenton):  We should make the ContextFactory live until there are
    //   no more references, rather than being owned by the evaluator.  However,
    //   this also implies that all the objects that the ContextFactory uses
    //   need to live, meaning some refactoring is needed.
    MutexLock lock(&mutex_);
    --refcount_;
  }

  virtual vm::Context::Environment NewContext() {
    AddReference();

    MutexLock lock(&mutex_);
    vm::Context::Environment result;
    if (mode_ == SINGLE_CONTEXT) {
      GOOGLE_CHECK(!main_memory_manager_in_use_)
          << "Cannot create multiple Contexts at one time when using debug "
             "memory manager.";
      // Use the main MemoryManager.
      result.memory_manager = evaluator_->memory_manager_.get();
      main_memory_manager_in_use_ = true;
    } else {
      // BrainDeadMemoryManagers don't care if they point at each other's
      // memory.
      //
      // TODO(kenton):  Multi-threaded memory managers.

      if (additional_memory_managers_.empty()) {
        // Create a new BraindeadMemoryManager for this context.
        // PoolAllocator is not thread-safe, so create a new one to use with
        // this MemoryManager.
        vm::memory::Allocator* allocator = new vm::memory::PoolAllocator(
            evaluator_->allocator_.get());
        result.memory_manager = new vm::memory::BraindeadMemoryManager(
            allocator);
        allocators_.push_back(allocator);
      } else {
        // Use one of the BraindeadMemoryManagers we created previously.
        result.memory_manager = additional_memory_managers_.back();
        additional_memory_managers_.pop_back();
      }
    }
    result.atom_registry = evaluator_->atom_registry_.get();
    result.support_code_manager = evaluator_->support_code_manager_;
    result.evaluator = evaluator_;
    return result;
  }

  virtual void ReleaseContext(const vm::Context::Environment& environment) {
    {
      MutexLock lock(&mutex_);
      if (mode_ == SINGLE_CONTEXT) {
        GOOGLE_CHECK(main_memory_manager_in_use_);
        main_memory_manager_in_use_ = false;
      } else {
        // Keep this MemoryManager for later reuse.  (We can't delete it because
        // that would destroy all of its contents and we don't know if there
        // are outstanding references to it.)
        additional_memory_managers_.push_back(environment.memory_manager);
      }
    }

    RemoveReference();
  }

 private:
  // This object needs its own mutex since it is sometimes called with
  // evaluator_->mutex_ already locked and sometimes without.
  Mutex mutex_;

  const Mode mode_;
  EvlanEvaluator* const evaluator_;
  vector<vm::memory::MemoryManager*> additional_memory_managers_;
  vector<vm::memory::Allocator*> allocators_;
  int refcount_;
  bool main_memory_manager_in_use_;
};

// ===================================================================

EvlanEvaluator::Options::Options()
  : use_debug_memory_manager_(false),
    no_support_code_(false) {}

EvlanEvaluator::Options::~Options() {}

void EvlanEvaluator::Options::SetSupportCode(const string& code) {
  support_code_.reset(new Module);
  parser::Parse(code, support_code_.get());
  no_support_code_ = false;
}

void EvlanEvaluator::Options::SetSupportCode(const Module& module) {
  support_code_.reset(new Module);
  support_code_->CopyFrom(module);
  no_support_code_ = false;
}

void EvlanEvaluator::Options::UseNoSupportCode() {
  support_code_.reset();
  no_support_code_ = true;
}

EvlanEvaluator::EvlanEvaluator()
  : parent_(NULL),
    cpp_glue_(new CppGlue(this)),
    allocator_(new vm::memory::MallocAllocator),
    pool_allocator_(new vm::memory::PoolAllocator(allocator_.get())),
    memory_manager_(
      new vm::memory::BraindeadMemoryManager(pool_allocator_.get())),
    atom_registry_(new vm::builtin::AtomRegistry),
    context_factory_(new ContextFactoryImpl(this,
                       ContextFactoryImpl::MULTIPLE_CONTEXTS)),
    support_code_manager_(new vm::builtin::SupportCodeManager),
    shutting_down_(false),
    has_forked_(false) {
  LoadEmbeddedSupportCode();
}

EvlanEvaluator::EvlanEvaluator(const Options& options)
  : parent_(NULL),
    cpp_glue_(new CppGlue(this)),
    atom_registry_(new vm::builtin::AtomRegistry),
    context_factory_(new ContextFactoryImpl(this,
                       options.use_debug_memory_manager_ ?
                         ContextFactoryImpl::SINGLE_CONTEXT :
                         ContextFactoryImpl::MULTIPLE_CONTEXTS)),
    support_code_manager_(new vm::builtin::SupportCodeManager),
    shutting_down_(false),
    has_forked_(false) {
  if (options.use_debug_memory_manager_) {
    memory_manager_.reset(new vm::memory::DebugMemoryManager);
  } else {
    allocator_.reset(new vm::memory::MallocAllocator);
    pool_allocator_.reset(new vm::memory::PoolAllocator(allocator_.get()));
    memory_manager_.reset(
      new vm::memory::BraindeadMemoryManager(pool_allocator_.get()));
  }

  if (!options.no_support_code_) {
    if (options.support_code_ == NULL) {
      LoadEmbeddedSupportCode();
    } else {
      scoped_ptr<EvlanValue> support_code_value(
        Evaluate(*options.support_code_));
      SetSupportCode(support_code_value.get());
    }
  }
}

EvlanEvaluator::EvlanEvaluator(EvlanEvaluator* parent)
  : parent_(parent),
    cpp_glue_(new CppGlue(parent_->cpp_glue_.get(), this)),
    allocator_(new vm::memory::MallocAllocator),
    pool_allocator_(new vm::memory::PoolAllocator(allocator_.get())),
    memory_manager_(
      new vm::memory::BraindeadMemoryManager(pool_allocator_.get())),
    atom_registry_(
      new vm::builtin::AtomRegistry(parent_->atom_registry_.get())),
    context_factory_(new ContextFactoryImpl(this,
                       ContextFactoryImpl::MULTIPLE_CONTEXTS)),
    support_code_manager_(parent_->support_code_manager_),
    shutting_down_(false),
    has_forked_(false) {
}

EvlanEvaluator::~EvlanEvaluator() {
  shutting_down_ = true;

  if (parent_ == NULL) {
    delete support_code_manager_;
  }
}

EvlanEvaluator* EvlanEvaluator::Fork() {
  MutexLock lock(&mutex_);
  has_forked_ = true;
  return new EvlanEvaluator(this);
}

// TODO(kenton):  Resource limits.
//void EvlanEvaluator::SetMemoryLimit(int64 limit);
//void EvlanEvaluator::SetInstructionLimit(int64 limit);

EvlanValue* EvlanEvaluator::Evaluate(const StringPiece& code) {
  // NO LOCK:  Inner call to Evaluate() will lock.
  NullImportResolver resolver;
  return Evaluate(code, &resolver);
}

EvlanValue* EvlanEvaluator::Evaluate(const Module& code) {
  // NO LOCK:  Inner call to Evaluate() will lock.
  NullImportResolver resolver;
  return Evaluate(code, &resolver);
}

EvlanValue* EvlanEvaluator::Evaluate(const StringPiece& code,
                                     ImportResolver* import_resolver) {
  // NO LOCK:  Inner call to Evaluate() will lock.
  Module module;
  parser::Parse(code.as_string(), &module);
  return Evaluate(module, import_resolver);
}

EvlanValue* EvlanEvaluator::Evaluate(const Module& code,
                                     ImportResolver* import_resolver) {
  // Use the ImportResolver to resolve all imports.  We must keep them in
  // EvlanValue form until we've loaded all of them in order to avoid having
  // them be garbage collected.
  scoped_array<EvlanValue*> import_values(new EvlanValue*[code.imports_size()]);
  for (int i = 0; i < code.imports_size(); i++) {
    import_values[i] = import_resolver->Import(this, code.imports(i));
  }

  // Couldn't lock until here because import_resolver->Import() may call us
  // back.
  MutexLock lock(&mutex_);
  CheckNotForked();

  // Set up the context.
  vm::Context context(context_factory_.get());

  // Now that we've loaded all the imports we can build the map which we pass
  // to the interpreter.
  map<string, vm::Value> interpreter_imports;
  for (int i = 0; i < code.imports_size(); i++) {
    // import_resolver->Import() may have returned NULL if the import was not
    // found.  In that case, we simply won't insert the import into the map,
    // and the interpreter will produce an appropriate error when it can't find
    // it.
    if (import_values[i] != NULL) {
      interpreter_imports[code.imports(i)] =
          import_values[i]->value_->Get(memory_manager_.get());
      delete import_values[i];
    }
  }
  interpreter_imports["builtin"] = vm::builtin::GetBuiltinLibrary();

  // Run the interpreter.
  EvlanValue* result = new EvlanValue(this);
  vm::Value callback(&EvlanValue::DoneCallback::kSingleton, vm::Data(result));
  vm::interpreter::InterpretModule(
    &context, code, NULL, interpreter_imports, callback);

  // Unlock while running the function so that other stuff can happen in
  // parallel.
  mutex_.Unlock();
  context.Run();
  mutex_.Lock();

  // Block until result is set.
  // TODO(kenton):  Acutally unlock mutex_ and wait if IsReady() is false.
  GOOGLE_CHECK(result->IsReady());

  return result;
}

EvlanValue* EvlanEvaluator::NewInteger(int value) {
  MutexLock lock(&mutex_);
  CheckNotForked();
  return new EvlanValue(this, vm::builtin::MakeInteger(value));
}

EvlanValue* EvlanEvaluator::NewDouble(double value) {
  MutexLock lock(&mutex_);
  CheckNotForked();
  return new EvlanValue(this, vm::builtin::MakeDouble(value));
}

EvlanValue* EvlanEvaluator::NewBoolean(bool value) {
  MutexLock lock(&mutex_);
  CheckNotForked();
  return new EvlanValue(this, vm::builtin::BooleanUtils::MakeBoolean(value));
}

EvlanValue* EvlanEvaluator::NewAtom(const StringPiece& name) {
  MutexLock lock(&mutex_);
  CheckNotForked();
  return new EvlanValue(this,
    atom_registry_->GetAtom(memory_manager_.get(), name));
}

EvlanValue* EvlanEvaluator::NewString(const StringPiece& value) {
  MutexLock lock(&mutex_);
  CheckNotForked();
  vm::memory::NullMemoryRoot root;
  return new EvlanValue(this,
    vm::builtin::ByteUtils::CopyString(
      memory_manager_.get(), &root, value));
}

EvlanValue* EvlanEvaluator::NewProtobuf(const google::protobuf::Message* value) {
  MutexLock lock(&mutex_);
  CheckNotForked();
  return new EvlanValue(this,
    vm::builtin::ProtobufUtils::AdoptMessage(memory_manager_.get(), value));
}

EvlanValue* EvlanEvaluator::NewProtoFile(const google::protobuf::FileDescriptor* file,
                                         google::protobuf::MessageFactory* factory,
                                         Mutex* factory_lock) {
  MutexLock lock(&mutex_);
  CheckNotForked();
  vm::memory::NullMemoryRoot root;
  return new EvlanValue(this,
    vm::builtin::ProtobufUtils::MakeFile(
      memory_manager_.get(), &root, file, factory, factory_lock, NULL));
}

EvlanValue* EvlanEvaluator::NewProtobufReference(const google::protobuf::Message& value) {
  MutexLock lock(&mutex_);
  CheckNotForked();
  return new EvlanValue(this,
    vm::builtin::ProtobufUtils::WrapMessage(memory_manager_.get(), &value));
}

EvlanValue* EvlanEvaluator::NewTuple(const vector<EvlanValue*>& elements) {
  MutexLock lock(&mutex_);
  CheckNotForked();

  if (elements.size() == 1) {
    // A size-one tuple is just a value.
    return new EvlanValue(this,
        elements[0]->value_->Get(memory_manager_.get()));
  }

  vm::memory::NullMemoryRoot root;
  vm::Value* array = memory_manager_->AllocateArray<vm::Value>(
    elements.size(), &root);
  for (int i = 0; i < elements.size(); i++) {
    array[i] = elements[i]->value_->Get(memory_manager_.get());
  }
  vm::Value array_value = vm::builtin::ArrayUtils::AliasArray(
    memory_manager_.get(), &root, elements.size(), array);
  return new EvlanValue(this,
    vm::builtin::ArrayUtils::ArrayToTuple(array_value));
}

EvlanValue* EvlanEvaluator::NewArray(const vector<EvlanValue*>& elements) {
  MutexLock lock(&mutex_);
  CheckNotForked();

  vm::memory::NullMemoryRoot root;
  vm::Value* array = memory_manager_->AllocateArray<vm::Value>(
    elements.size(), &root);
  for (int i = 0; i < elements.size(); i++) {
    array[i] = elements[i]->value_->Get(memory_manager_.get());
  }
  vm::Value array_value = vm::builtin::ArrayUtils::AliasArray(
    memory_manager_.get(), &root, elements.size(), array);
  return new EvlanValue(this, array_value);
}

EvlanValue* EvlanEvaluator::NewCustomValue(CustomValue* value) {
  MutexLock lock(&mutex_);
  CheckNotForked();
  return new EvlanValue(this,
    vm::Value(&CustomValueLogic::kSingleton,
              vm::Data(new TrackedCustomValue(memory_manager_.get(), value))));
}

EvlanValue* EvlanEvaluator::NewNonBlockingCustomValue(
    NonBlockingCustomValue* value) {
  MutexLock lock(&mutex_);
  CheckNotForked();
  return new EvlanValue(this,
    vm::Value(&NonBlockingCustomValueLogic::kSingleton,
              vm::Data(new TrackedNonBlockingCustomValue(
                memory_manager_.get(), value))));
}

EvlanValue* EvlanEvaluator::NewTypeError(const StringPiece& description) {
  MutexLock lock(&mutex_);
  CheckNotForked();
  vm::memory::NullMemoryRoot root;
  return new EvlanValue(this,
    vm::builtin::TypeErrorUtils::MakeError(
      memory_manager_.get(), &root, description));
}

EvlanValue* EvlanEvaluator::CloneValue(EvlanValue* value) {
  MutexLock lock(&mutex_);
  GOOGLE_DCHECK(this->IsDescendantOf(value->evaluator_));
  return new EvlanValue(this, value->value_->Get(memory_manager_.get()));
}

vm::ContextFactory* EvlanEvaluator::InternalGetContextFactory() {
  context_factory_->AddReference();
  return context_factory_.get();
}

EvlanValue* EvlanEvaluator::InternalNewValue(
    vm::memory::MemoryManager* memory_manager,
    vm::Value value) {
  return new EvlanValue(this, value, memory_manager);
}

void EvlanEvaluator::SetSupportCode(EvlanValue* value) {
  MutexLock lock(&mutex_);
  GOOGLE_CHECK(parent_ == NULL) << "Can only call SetSupportCode() on root VM.";

  vm::Context context(context_factory_.get());

  support_code_manager_->Init(&context,
      value->value_->Get(memory_manager_.get()));
}

void EvlanEvaluator::LoadEmbeddedSupportCode() {
  // NO LOCK:  We call other methods that lock as necessary.
  StringPiece bytecode = GetBuiltinSupportBytecode();
  Module module;
  if (module.ParseFromArray(bytecode.data(), bytecode.size())) {
    scoped_ptr<EvlanValue> support_code_value(Evaluate(module));
    SetSupportCode(support_code_value.get());
  } else {
    GOOGLE_LOG(DFATAL) << "Couldn't parse Evlan support module.";
  }
}

EvlanEvaluator* EvlanEvaluator::FindCommonEvaluator(EvlanEvaluator* other) {
  // NO LOCK:  parent_ pointer cannot change after construction and thus need
  //   not be protected.
  if (IsDescendantOf(other)) {
    return this;
  } else if (other->IsDescendantOf(this)) {
    return other;
  } else {
    return NULL;
  }
}

bool EvlanEvaluator::IsDescendantOf(EvlanEvaluator* other) {
  // NO LOCK:  parent_ pointer cannot change after construction and thus need
  //   not be protected.
  EvlanEvaluator* ancestor = this;
  while (ancestor != NULL) {
    if (ancestor == other) return true;
    ancestor = ancestor->parent_;
  }
  return false;
}

void EvlanEvaluator::CheckNotForked() {
  mutex_.AssertHeld();
  if (has_forked_) {
    GOOGLE_LOG(DFATAL) << "An EvlanEvaluator cannot be used to construct or evaluate "
                   "new values after Fork() has been called.";
  }
}

EvlanEvaluator::MemoryStats EvlanEvaluator::GetMemoryStats() {
  MemoryStats stats = { allocator_->GetTotalBytesAllocated() };
  return stats;
}

}  // namespace evlan
